﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Media;
using System.Windows.Forms;
using TimerUppercut.Domain;
using TimerUppercut.Utils;
using Timer=TimerUppercut.Domain.Timer;

namespace TimerUppercut.Forms
{
    public partial class TimerForm : Form
    {
        private List<Timer> Counter { get; set;}
        private List<Timer> Timers { get; set; }
        private Timer Current { get; set; }
        private string defaultMessage { get; set;}
        private string defaultDesc { get; set; }
        private ulong StartTime;
        private ulong TotalTotalTime { get; set; }
        private string urlImport;
        private string urlExport;
        private int index;

        /// <summary>
        /// Initializes a new instance of the <see cref="TimerForm"/> class.
        /// </summary>
        public TimerForm()
        {
            InitializeComponent();
            dialogImport.Multiselect = false;

            string zeroTime = GetTimeLeft(0);
            defaultMessage = zeroTime;
            lblTimeTotal.Text = zeroTime;
            lblTimeLeft.Text = zeroTime;

            defaultDesc = string.Empty;
        }

        /// <summary>
        /// Handles the Load event of the TimerForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TimerForm_Load(object sender, EventArgs e)
        {
            Text = string.Format("{0} {1}", AssemblyInfo.AssemblyTitle, AssemblyInfo.AssemblyVersion);
            lblTimer.Text = defaultMessage;
            lblDescBig.Text = defaultDesc;
            lblDescBig.Text = string.Empty;

            var restored = Properties.Settings.Default.LastOpenedFile;
            if (File.Exists(restored))
            {
                var result = Speaker.Ask(string.Format("Previous session found.\nRestore \"{0}\"?", Path.GetFileNameWithoutExtension(restored)));
                if (result == DialogResult.OK)
                {
                    urlImport = restored;
                    ImportXML();
                }
                else
                {
                    Properties.Settings.Default.LastOpenedFile = string.Empty;
                    Properties.Settings.Default.Save();
                }
            }

            SetGlobalTooltips();
        }

        /// <summary>
        /// Sets the global tooltips.
        /// </summary>
        private void SetGlobalTooltips()
        {
            toolTip.SetToolTip(btnStart, FormatShortcutKeys(tsStart.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnStop, FormatShortcutKeys(tsStop.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnClear, FormatShortcutKeys(tsReset.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnApply, FormatShortcutKeys(tsRegisterApply.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnCancel, FormatShortcutKeys(tsRegisterCancel.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnNew, FormatShortcutKeys(tsRegisterNew.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnFileImport, FormatShortcutKeys(tsImport.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnFileExport, FormatShortcutKeys(tsExport.ShortcutKeys.ToString()));
            toolTip.SetToolTip(btnFileReload, FormatShortcutKeys(tsReload.ShortcutKeys.ToString()));
        }

        /// <summary>
        /// Formats the shortcut keys.
        /// </summary>
        /// <param name="shortcutKeys">The shortcut keys.</param>
        /// <returns></returns>
        private static string FormatShortcutKeys(string shortcutKeys)
        {
            var keys = shortcutKeys.Split(',');

            // All shortkeys should be composed of two keys
            var final = keys[1].Trim() + "+" + keys[0].Trim();
            return final;
        }

        /// <summary>
        /// Handles the Click event of the btnNew control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnNew_Click(object sender, EventArgs e)
        {
            if (btnNew.Enabled)
            {
                SetGbRegisterButtons(true);
                numericHours.Text = string.Empty;
                numericMinutes.Text = string.Empty;
                numericSeconds.Text = string.Empty;
                txtDesc.Enabled = true;
                numericHours.Focus();
            }
        }

        /// <summary>
        /// Handles the Click event of the btnCancel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (btnCancel.Enabled)
            {
                SetGbRegisterButtons(false); 
            }
        }

        /// <summary>
        /// Sets the register buttons.
        /// </summary>
        /// <param name="enable">if set to <c>true</c> [enable].</param>
        private void SetGbRegisterButtons(bool enable)
        {
            numericHours.Enabled = enable;
            numericMinutes.Enabled = enable;
            numericSeconds.Enabled = enable;
            txtDesc.Enabled = enable;
            btnApply.Enabled = enable;
            btnCancel.Enabled = enable;

            numericHours.Text = "0";
            numericMinutes.Text = "0";
            numericSeconds.Text = "0";
            txtDesc.Text = string.Empty;

            btnNew.Enabled = !enable;
        }

        /// <summary>
        /// Plays the sound.
        /// </summary>
        /// <param name="any">if set to <c>true</c> [there are counters left].</param>
        private static void PlaySound(bool any)
        {
            string next = string.Format("{0}\\Resources\\next.wav", Directory.GetCurrentDirectory());
            string finished = string.Format("{0}\\Resources\\finished.wav", Directory.GetCurrentDirectory());
            var simpleSound = new SoundPlayer(any ? next : finished);
            simpleSound.Play();
        }

        /// <summary>
        /// Handles the Click event of the btnApply control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnApply_Click(object sender, EventArgs e)
        {
            if (btnApply.Enabled)
            {
                var hours = (ulong)numericHours.Value;
                var minutes = (int)numericMinutes.Value;
                var seconds = (int)numericSeconds.Value;
                var description = txtDesc.Text.Trim();

                bool zeroValues = (hours == 0) && (minutes == 0) && (seconds == 0);
                if (!zeroValues)
                {
                    if (string.IsNullOrEmpty(description))
                    {
                        Speaker.Throw("Entry description must be set");
                        return;
                    }
                    var timer = new Timer
                                    {
                                        Hours = hours,
                                        Minutes = minutes,
                                        Seconds = seconds,
                                        Description = description
                                    };

                    if (Timers == null)
                    {
                        Timers = new List<Timer>();
                    }

                    timer.Id = Guid.NewGuid();
                    Timers.Add(timer);

                    BindGridView();
                    SetTotalTimeStatusText();
                    SetGbRegisterButtons(false);
                }
                else
                {
                    Speaker.Throw("Unable to add empty timer");
                } 
            }
        }

        /// <summary>
        /// Sets the total time status.
        /// </summary>
        private void SetTotalTimeStatusText()
        {
            ulong totalTime = 0;
            Timers.ForEach(x => { totalTime += x.TotalSeconds; });
            var time = GetTimeLeft(totalTime);
            lblTimeTotal.Text = time;
            lblTimeLeft.Text = time;
        }

        /// <summary>
        /// Binds the grid view.
        /// </summary>
        private void BindGridView()
        {
            timerBindingSource.DataSource = null;
            timerBindingSource.DataSource = Timers;
            gvTimers.DataSource = timerBindingSource;
        }

        /// <summary>
        /// Handles the CellClick event of the gvTimers control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void gvTimers_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            const int buttonColumn = 0;
            if (e.ColumnIndex == buttonColumn)
            {
                RemoveTimer(e);
            }
        }

        /// <summary>
        /// Handles the CellDoubleClick event of the gvTimers control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void gvTimers_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            RemoveTimer(e);
        }

        /// <summary>
        /// Handles the KeyDown event of the gvTimers control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void gvTimers_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                PerformRemoval();
            }
        }

        /// <summary>
        /// Performs the removal of the selected entry.
        /// </summary>
        private void PerformRemoval()
        {
            var selection = (Timer) timerBindingSource.Current;
            if (selection != null)
            {
                var newList = from t in Timers
                              where t.Id != selection.Id
                              select t;

                Timers = newList.ToList();
                SetTotalTimeStatusText();
                timerBindingSource.DataSource = Timers;
            }
        }

        /// <summary>
        /// Removes the timer entry.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void RemoveTimer(DataGridViewCellEventArgs e)
        {
            if (e.RowIndex != -1)
            {
                PerformRemoval();
            }
        }

        /// <summary>
        /// Gets the time left.
        /// </summary>
        /// <param name="seconds">The seconds.</param>
        /// <returns></returns>
        private static string GetTimeLeft(ulong seconds)
        {
            TimeSpan t = TimeSpan.FromSeconds(seconds);
            //:{3:D3}ms
            return string.Format("{0:D2}h:{1:D2}m:{2:D2}s",
                                 t.Hours,
                                 t.Minutes,
                                 t.Seconds);
        }

        /// <summary>
        /// Handles the Click event of the btnFileImport control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnFileImport_Click(object sender, EventArgs e)
        {
            if (btnFileImport.Enabled)
            {
                if (dialogImport.ShowDialog() == DialogResult.OK)
                {
                    urlImport = dialogImport.FileName;
                    ImportXML();
                    btnFileReload.Enabled = true;
                    Properties.Settings.Default.LastOpenedFile = urlImport;
                    Properties.Settings.Default.Save();
                } 
            }
        }

        /// <summary>
        /// Handles the Click event of the btnFileExport control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnFileExport_Click(object sender, EventArgs e)
        {
            if (btnFileExport.Enabled)
            {
                if (dialogExport.ShowDialog() == DialogResult.OK)
                {
                    urlExport = dialogExport.FileName;
                    GetExportData();

                    Properties.Settings.Default.LastOpenedFile = urlExport;
                    Properties.Settings.Default.Save();
                }
            }
        }

        /// <summary>
        /// Imports the XML.
        /// </summary>
        private void ImportXML()
        {
            if (string.IsNullOrEmpty(urlImport)) return;

            var restore = XMLHelper.LoadXML(urlImport);
            Timers = restore.Timers.ToList();
            gbTimers.Text = string.Format("Timer: {0} ({1})", Path.GetFileNameWithoutExtension(restore.Name), restore.Date);
            BindGridView();
            SetTotalTimeStatusText();
            btnFileReload.Enabled = true;
        }

        /// <summary>
        /// Gets the export data.
        /// </summary>
        private void GetExportData()
        {
            var restore = new Restore {Name = (urlExport), Date = DateTime.Now, Timers = Timers ?? new List<Timer>()};

            XMLHelper.GenerateXML(restore);
        }

        /// <summary>
        /// Handles the KeyDown event of the numericHours control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void numericHours_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                numericMinutes.Focus(); 
            }
        }

        /// <summary>
        /// Handles the KeyDown event of the numericMinutes control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void numericMinutes_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                numericSeconds.Focus();
            }
        }

        /// <summary>
        /// Handles the KeyDown event of the numericSeconds control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void numericSeconds_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                txtDesc.Focus();
            }
        }

        /// <summary>
        /// Handles the Click event of the btnStop control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            if (btnStop.Enabled)
            {
                timer1.Stop();

                SetStopControls();
                index = 0;
                SelectRow(index); 
            }
        }

        /// <summary>
        /// Sets the stop controls.
        /// </summary>
        private void SetStopControls()
        {
            lblTimer.Text = defaultMessage;
            lblTimer.ForeColor = Color.Black;
            lblDescBig.Text = defaultDesc;

            btnStop.Enabled = false;
            btnStart.Enabled = true;
            btnClear.Enabled = true;
            gbData.Enabled = true;
            gbRegister.Enabled = true;
            gvTimers.Enabled = true;
        }

        /// <summary>
        /// Handles the Tick event of the timer1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            StartTime--;
            TotalTotalTime--;

            lblTimer.Text = GetTimeLeft(StartTime);
            lblTimeLeft.Text = GetTimeLeft(TotalTotalTime);
            
            if (StartTime == 0)
            {
                timer1.Stop();
                Counter.Remove(Current);
                lblTimer.ForeColor = Color.Black;
                
                if (Counter.Any())
                {
                    PlaySound(true);
                    Current = Counter.First();
                    lblDescBig.Text = Current.Description;
                    StartTime = Current.TotalSeconds;
                    index++;
                    SelectRow(index);
                    timer1.Start();
                }
                else
                {
                    PlaySound(false);
                    lblTimer.Text = defaultMessage;
                    lblDescBig.Text = defaultDesc;
                    lblTimeLeft.Text = lblTimeTotal.Text;
                    SetStopControls();
                    index = 0;
                    SelectRow(index);
                    //EventUtility.WriteEvent(" ", "Automatic Stop", EventLogEntryType.Error);
                }
            }
            else if (StartTime <= 10)
            {
                lblTimer.ForeColor = Color.Red;
            }
        }

        /// <summary>
        /// Selects the row.
        /// </summary>
        /// <param name="_index">The _index.</param>
        private void SelectRow(int _index)
        {
            gvTimers.Rows[_index].Selected = true;
        }

        /// <summary>
        /// Handles the Click event of the btnStart control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            if (btnStart.Enabled)
            {
                if (Timers == null) return;

                Counter = new List<Timer>(Timers);

                if (Counter.Any())
                {
                    btnClear.Enabled = false;
                    btnStop.Enabled = true;
                    btnStart.Enabled = false;
                    gbData.Enabled = false;
                    gbRegister.Enabled = false;
                    gvTimers.Enabled = false;

                    Current = Counter.First();
                    StartTime = Current.TotalSeconds;
                    TotalTotalTime = 0;
                    Timers.ForEach(x => { TotalTotalTime += x.TotalSeconds; });
                    lblTimer.Text = GetTimeLeft(StartTime);
                    lblDescBig.Text = Current.Description;

                    //EventUtility.WriteEvent("(Event log)", "Timer Start", EventLogEntryType.Information);
                    SelectRow(index);
                    //Thread.Sleep(2000);
                    timer1.Start();
                }
                else
                {
                    PlaySound(false);
                } 
            }
        }

        /// <summary>
        /// Handles the Click event of the btnClear control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnClear_Click(object sender, EventArgs e)
        {
            if (btnClear.Enabled)
            {
                DialogResult result = Speaker.Ask("Clear all entries?");
                if (result == DialogResult.OK)
                {
                    Timers = new List<Timer>();
                    timerBindingSource.DataSource = Timers;
                    gvTimers.DataSource = timerBindingSource;
                } 
            }
        }

        /// <summary>
        /// Handles the KeyDown event of the txtDesc control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void txtDesc_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                btnApply.Focus();
            }
        }

        /// <summary>
        /// Handles the KeyDown event of the btnApply control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void btnApply_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                btnNew.Focus();
            }
        }

        /// <summary>
        /// Handles the Click event of the btnFileReload control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnFileReload_Click(object sender, EventArgs e)
        {
            if (btnFileReload.Enabled)
            {
                if (Speaker.Ask("Revert changes and reload?") == DialogResult.OK)
                {
                    ImportXML();
                } 
            }
        }

        /// <summary>
        /// Handles the Click event of the aboutToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (var form = new AboutBox())
            {
                form.ShowDialog();
            }
        }

        /// <summary>
        /// Handles the Click event of the tsExit control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Displays messageboxes in a pretty way.
        /// </summary>
        private static class Speaker
        {
            /// <summary>
            /// Throws the specified message.
            /// </summary>
            /// <param name="message">The message.</param>
            public static void Throw(string message)
            {
                MessageBox.Show(message, "Error dialog", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

            /// <summary>
            /// Asks the specified question.
            /// </summary>
            /// <param name="question">The question.</param>
            /// <returns></returns>
            public static DialogResult Ask(string question)
            {
                return MessageBox.Show(question, "Question dialog", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
            }
        }

        /// <summary>
        /// Handles the FormClosing event of the TimerForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void TimerForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (Speaker.Ask("Are sure you want to quit?") == DialogResult.Cancel)
            {
                e.Cancel = true;
            }
        }
    }
}